#pragma once

/// <summary>
/// Camera class: Encapsulates camera properties, frustum, and attached character.
/// Methods for walking, strafing, dollying and rotating the camera.
/// Supports third-person and first-person modes.
/// </summary>

/**	Author: Dane Bulat
  * Created: 06/2013
  * Last Modified: 30/06/2013
  * Version: v1.0
  */

#include "common/d3dUtil.h"
#include "BasicMesh.h"

class Camera
{
public:
	Camera();
	~Camera();

	//=================================================
	// Public Interface Methods
	//=================================================

	/**
	  * Initialise
	  *
      * Initialise method that calculates the camera clamping values for rotation.
	  */
	void			Initialise();

	/**
	  * UpdateView
	  *
      * Updates the camera view matrix.
	  */
	void			UpdateView();

	/**
	  * UpdateRotation
	  *
      * Updates the camera rotational values
	  * @param Float representing horizontal rotation along spherical coordinates.
	  * @param Fload representing vertical rotation along spherical coordinates.
	  */
	void			UpdateRotation(float theta, float phi);

	/**
	  * UpdateDistance
	  *
      * Updates camera's distance relative to the character.
	  * @param Float representing the camera's distance from the character.
	  */
	void			UpdateDistance(float d);
	
	/**
	  * Walk
	  *
      * Moves the camera such that it stays relative to the character.
	  * @param Float representing the magnitude to move the camera.
	  */
	void			Walk(float d);

	/**
	  * Walk
	  *
      * Moves the camera such that it stays relative to the character.
	  * @param Float representing the magnitude to move the camera.
	  * @param Vector representing the character's move velocity.
	  */
	void			Walk(float d, XMFLOAT3& v);					// Walk camera based on velocity.

	/**
	  * Strafe
	  *
      * Moves the camera such that it stays relative to the character.
	  * @param Float representing the magnitude to strafe the camera.
	  */
	void			Strafe(float d);

	/**
	  * Strafe
	  *
      * Moves the camera such that it stays relative to the character.
	  * @param Float representing the magnitude to strafe the camera.
	  * @param Vector representing the character's strafe velocity.
	  */
	void			Strafe(float d, XMFLOAT3& v);

	/**
	  * ToggleThirdPerson
	  *
      * Switches the camera to third-person mode.
	  */
	void			ToggleThirdPerson();

	/**
	  * ToggleFirstPerson
	  *
      * Switches the camera to first-person mode.
	  */
	void			ToggleFirstPerson();
	
	/**
	  * IsThirdPerson
	  *
      * Returns true if the camera is in third-person mode; false otherwise.
	  */
	bool			IsThirdPerson() const;
	
	//=================================================
	// Setters
	//=================================================

	/**
	  * SetCharacter
	  *
      * Sets the character that the camera will be bound to.
	  * @param BasicMesh representing the character.
	  */
	void			SetCharacter(BasicMesh *character);

	/**
	  * SetLens
	  *
      * Constructs the camera's projection matrix.
	  * @param Float representing the camera FOV (Field Of View).
	  * @param Float representing the camera aspect ratio.
	  * @param Float representing the camera near plane.
	  * @param Float representing the camera far plane.
	  */
	void			SetLens(float fov_y, float aspect, float zn, float zf);

	/**
	  * SetTargetOffset
	  *
      * Sets the offset distance relative to the character that the camera will point to.
	  * @param Float representing the offset distance.
	  */
	void			SetTargetOffset(float offset);

	/**
	  * SetDistance
	  *
      * Sets the distance that the camera will be from the character.
	  * @param Float representing camera distance (radius) from the character.
	  */
	void			SetDistance(float distance);

	//=================================================
	// Getters
	//=================================================

	/**
	  * GetPosition
	  *
      * @return Returns the camera position vector.
	  */
	XMFLOAT3		GetPosition() const;

	/**
	  * GetTargetOffset
	  *
      * @return Returns the camera target offset from character.
	  */
	float			GetTargetOffset() const;

	/**
	  * GetDistance
	  *
      * @return Returns the distance (radius) from the camera to the character.
	  */
	float			GetDistance() const;
	
	/**
	  * GetFrustum
	  *
      * @return Returns the camera frustum object.
	  */
	XNA::Frustum*	GetFrustum() const;

	/**
	  * View
	  *
      * @return Returns the camera view matrix.
	  */
	XMMATRIX		View() const;

	/**
	  * Proj
	  *
      * @return Returns the camera projection matrix.
	  */
	XMMATRIX		Proj() const;

	/**
	  * ViewProj
	  *
      * @return Returns the camera view-projection matrix.
	  */
	XMMATRIX		ViewProj() const;
	
private:
	//=================================================
	// Third-Person Mode Methods [private]
	//=================================================

	/**
	  * TPUpdateView
	  *
      * Updates the camera's view matrix if the camera is in third-person mode.
	  */
	void			TPUpdateView();

	/**
	  * TPUpdateRotation
	  *
      * Updates the third-person camera rotation.
	  */
	void			TPUpdateRotation();

	/**
	  * TPStrafe
	  *
      * Strafes the camera/character in third-person mode.
	  * @param Float representing the distance to strafe.
	  */
	void			TPStrafe(float d);

	/**
	  * TPStrafe
	  *
      * Strafes the camera/character in third-person mode based on character's velocity.
	  * @param Float representing the distance to strafe.
	  * @param Vector representing the character's strafe velocity.
	  */
	void			TPStrafe(float d, XMFLOAT3& currentVelocity);

	/**
	  * TPWalk
	  *
      * Moves the camera/character forward/backward in third-person mode.
	  * @param Float representing the distance to walk.
	  */
	void			TPWalk(float d);

	/**
	  * TPWalk
	  *
      * Moves the camera/character forward/backward in third-person mode based on character's velocity.
	  * @param Float representing the distance to walk.
	  * @param Vector representing the character's strafe velocity.
	  */
	void			TPWalk(float d, XMFLOAT3& currentVelocity);

	//=================================================
	// First-Person Mode Methods [private]
	//=================================================

	/**
	  * FPUpdateView
	  *
      * Updates the camera's view matrix if the camera is in first-person mode.
	  */
	void			FPUpdateView();

	/**
	  * TPUpdateRotation
	  *
      * Updates the first-person camera rotation.
	  * @param Float representing the horizontal amount to rotate.
	  * @param Float representing the vertical amount to rotate.
	  */
	void			FPUpdateRotation(float theta, float phi);

	/**
	  * FPStrafe
	  *
      * Strafes the camera/character in first-person mode.
	  * @param Float representing the distance to strafe.
	  */
	void			FPStrafe(float d);

	/**
	  * FPStrafe
	  *
      * Strafes the camera/character in first-person mode based on character's velocity.
	  * @param Float representing the distance to strafe.
	  * @param Vector representing the character's strafe velocity.
	  */
	void			FPStrafe(float d, XMFLOAT3& currentVelocity);
	
	/**
	  * FPWalk
	  *
      * Moves the camera/character forward/backward in first-person mode.
	  * @param Float representing the distance to walk.
	  */
	void			FPWalk(float d);

	/**
	  * FPWalk
	  *
      * Moves the camera/character forward/backward in first-person mode based on character's velocity.
	  * @param Float representing the distance to walk.
	  * @param Vector representing the character's strafe velocity.
	  */
	void			FPWalk(float d, XMFLOAT3& currentVelocity);

private:
	bool			m_bThirdPerson;								///< Flag representing if camera is in third person mode.

	// Cache View and Projection matrices.
	XMFLOAT4X4		m_view;										///< The camera view matrix.
	XMFLOAT4X4		m_proj;										///< The camera projection matrix.

	// Third-Person spherical coordinate properties.
	float			m_theta;									///< The camera's current horizontal rotation.
	float			m_phi;										///< The camera's current vertical rotation.
	float			m_distance;									///< The camera's current distance from the character (radius).

	// First-person basis vectors.
	XMFLOAT3		m_fpRight;									///< The camera's first-person right basis vector.
	XMFLOAT3		m_fpUp;										///< The camera's first-person up basis vector.
	XMFLOAT3		m_fpLook;									///< The camera's first-person look basis vector.

	// Camera properties.
	XMFLOAT3		m_position;									///< The camera's position.
	float			m_characterRotY;							///< The character's rotation Y value in degrees.
	float			m_targetOffset;								///< The offset value from the character the camera will point to.

	// Camera frustum
	XNA::Frustum*	m_Frustum;									///< The camera view frustum.

	// Third-person and first-person phi clamping angles.
	float			m_tpPhiClampMin;							///< The camera's minimum vertical rotation in third-person mode (degrees).
	float			m_tpPhiClampMax;							///< The camera's maximum vertical rotation in third-person mode (degrees).
	float			m_fpPhiClampMin;							///< The camera's minimum vertical rotation in first-person mode (degrees).
	float			m_fpPhiClampMax;							///< The camera's minimum vertical rotation in first-person mode (degrees).

	// Character properties.
	BasicMesh*		m_character;								///< Pointer to the character the camera is bound to.
	XMFLOAT3		m_characterLook;							///< The bound character's look vector.
	XMFLOAT3		m_characterRight;							///< The bound character's right vector.
	XMFLOAT3		m_characterUp;								///< The bound character's up vector.
};